package seatiger.tomcat.catalina.session;

import seatiger.tomcat.catalina.*;
import seatiger.tomcat.util.StringManager;

import java.io.IOException;
import java.util.List;

/**
 * session持久化管理，提供session的换出，复制，活化等功能
 * 可以根据不同的存储器 存储到不同的持久化介质中
 */
public class PersistentManager extends ManagerBase implements Lifecycle, Runnable {

    private boolean started = false;

    /**
     * 后台线程
     */
    private Thread thread = null;


    /**
     * 后台线程完成标志
     */
    protected boolean threadDone = false;


    /**
     * 后台线程名称
     */
    private String threadName = "PersistentManagerBase";

    private int checkInterval = 10;

    /**
     * 存储器
     */
    private Store store = null;

    /**
     * 在换出之前，空闲时间必须要大于该值
     */
    private int minIdleSwap = -1;

    /**
     * 如果达到了最大空闲时间，就可以换出了,如果是-1 就不换出
     */
    private int maxIdleSwap = -1;

    /**
     * 最大活动session数量
     */
    protected int maxActiveSessions = -1;

    /**
     * 最大空闲备份时间,如果超过了这个时间，session就备份到存储器中
     */
    private int maxIdleBackup = -1;

    /**
     * true 表示服务器关闭时，将保存session到存储器中
     */
    private boolean saveOnRestart = true;

    public void setCheckInterval(int checkInterval) {
        this.checkInterval = checkInterval;
    }

    public void setSaveOnRestart(boolean saveOnRestart) {
        this.saveOnRestart = saveOnRestart;
    }

    public Store getStore() {
        return store;
    }

    public void setMaxIdleBackup(int maxIdleBackup) {
        this.maxIdleBackup = maxIdleBackup;
    }

    public int getMaxIdleBackup() {
        return maxIdleBackup;
    }

    public void setMaxActiveSessions(int maxActiveSessions) {
        this.maxActiveSessions = maxActiveSessions;
    }

    public int getMaxActiveSessions() {
        return maxActiveSessions;
    }

    public void setMinIdleSwap(int minIdleSwap) {
        this.minIdleSwap = minIdleSwap;
    }

    public void setMaxIdleSwap(int maxIdleSwap) {
        this.maxIdleSwap = maxIdleSwap;
    }

    protected static StringManager sm = StringManager.getManager(Constants.PACKAGE_SESSION);

    public void setStore(Store store) {
        this.store = store;
    }

    @Override
    public void start() throws LifecycleException {
        if (started) {
            throw new LifecycleException
                    (sm.getString("standardManager.alreadyStarted"));
        }
        started = true;

        if (store != null && store instanceof Lifecycle) {
            ((Lifecycle) store).start();
        }
        threadStart();
    }

    protected void threadStart() {
        if (thread != null) {
            return;
        }
        threadDone = false;
        threadName = "StandardManager[" + "]";
        thread = new Thread(this, threadName);
        thread.setDaemon(true);
        thread.start();
    }

    @Override
    public void stop() throws LifecycleException {
        if (!started) {
            throw new LifecycleException(sm.getString("standardManager.notStarted"));
        }
        started = false;
        //停止后台线程
        threadStop();
        if (getStore() != null && saveOnRestart) {
            unload();
        } else {
            // Expire all active sessions
            Session sessions[] = findSessions();
            for (int i = 0; i < sessions.length; i++) {
                StandardSession session = (StandardSession) sessions[i];
                if (!session.isValid())
                    continue;
                session.expire();
            }
        }

        if (getStore() != null && getStore() instanceof Lifecycle) {
            ((Lifecycle) getStore()).stop();
        }
    }

    /**
     * 将内存中的所有没有过期的session放入存储器中
     */
    public void unload() {
        if (store == null) {
            return;
        }
        Session sessions[] = findSessions();
        int n = sessions.length;
        if (n == 0) {
            return;
        }
        for (int i = 0; i < n; i++) {
            try {
                swapOut(sessions[i]);
            } catch (IOException e) {
            }
        }

    }

    @Override
    public void run() {
        while (!threadDone) {
            threadSleep();
            processExpires();
            //具体的换出逻辑
            processPersistenceChecks();
        }
    }

    public void processPersistenceChecks() {
        processMaxIdleSwaps();
        processMaxActiveSwaps();
        processMaxIdleBackups();
    }

    /**
     * 将达到最大空闲时间的session备份到存储器中
     * 备份到存储器中的session，在内容中不会删除
     */
    protected void processMaxIdleBackups() {
        if (!started || maxIdleBackup < 0) {
            return;
        }
        Session sessions[] = findSessions();
        long timeNow = System.currentTimeMillis();

        if (maxIdleBackup >= 0) {
            for (int i = 0; i < sessions.length; i++) {
                StandardSession session = (StandardSession) sessions[i];
                if (!session.isValid())
                    continue;
                int timeIdle = // Truncate, do not round up
                        (int) ((timeNow - session.getLastAccessedTime()) / 1000L);
                if (timeIdle > maxIdleBackup) {
                    try {
                        writeSession(session);
                    } catch (IOException e) {
                        ;   // This is logged in writeSession()
                    }
                }
            }
        }
    }


    /**
     * 将达到空闲时间的session 换出
     */
    protected void processMaxIdleSwaps() {
        if (!started || maxIdleSwap < 0) {
            return;
        }
        Session[] sessions = findSessions();
        long timeNow = System.currentTimeMillis();
        for (Session session : sessions) {
            if (session.isValid()) {
                int timeIdle = (int) ((timeNow - session.getLastAccessedTime()) / 1000L);
                if (timeIdle > maxIdleSwap && timeIdle > minIdleSwap) {
                    try {
                        swapOut(session);
                    } catch (IOException e) {
                    }
                }
            }
        }
    }

    /**
     * 将多余的session，且空闲时间大于minIdleSwap 的session换出
     */
    protected void processMaxActiveSwaps() {
        if (!started || getMaxActiveSessions() < 0) {
            return;
        }
        Session[] sessions = findSessions();
        if (getMaxActiveSessions() > sessions.length) {
            return;
        }
        int toswap = sessions.length - getMaxActiveSessions();
        long timeNow = System.currentTimeMillis();
        for (int i = 0; i < sessions.length && toswap > 0; i++) {
            int timeIdle = (int) ((timeNow - sessions[i].getLastAccessedTime()) / 1000L);
            if (timeIdle > minIdleSwap) {
                try {
                    swapOut(sessions[i]);
                } catch (IOException e) {
                }
                toswap--;
            }
        }
    }

    /**
     * 将session换出
     *
     * @param session
     * @throws IOException
     */
    protected void swapOut(Session session) throws IOException {
        if (store == null && !session.isValid()) {
            return;
        }
        writeSession(session);
        super.remove(session);
    }

    protected void writeSession(Session session) throws IOException {
        if (store == null || !session.isValid())
            return;
        try {
            store.save(session);
        } catch (IOException e) {
            throw e;
        }
    }


    /**
     * 先将过期的session过滤掉
     */
    protected void processExpires() {
        if (!started) {
            return;
        }
        Session[] sessions = findSessions();
        for (int i = 0; i < sessions.length; i++) {
            StandardSession session = (StandardSession) sessions[i];
            session.isValid();
        }
    }

    @Override
    public void remove(Session session) {
        super.remove(session);
        if (store != null) {
            try {
                store.remove(session.getId());
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    @Override
    public Session findSession(String id) throws IOException {
        Session session = super.findSession(id);
        if (session != null) {
            return session;
        }

        return swapIn(id);
    }

    /**
     * 从文件中恢复session
     *
     * @param id
     * @return
     * @throws IOException
     */
    protected Session swapIn(String id) throws IOException {
        if (store == null) {
            return null;
        }

        Session session;
        try {
            session = store.load(id);
        } catch (ClassNotFoundException e) {
            throw new IllegalStateException
                    (sm.getString("persistentManager.deserializeError", id, e));
        }
        if (session == null) {
            return null;
        }
        if (!session.isValid()) {
            session.expire();
            store.remove(id);
        }
        session.setManager(this);
        add(session);
        return session;
    }

    protected void threadSleep() {
        try {
            Thread.sleep(checkInterval * 1000L);
        } catch (InterruptedException e) {
        }
    }

    protected void threadStop() {
        if (thread == null)
            return;
        threadDone = true;
        thread.interrupt();
        try {
            thread.join();
        } catch (InterruptedException e) {
            ;
        }

        thread = null;

    }

    @Override
    public void addLifecycleListener(LifecycleListener listener) {

    }

    @Override
    public List<LifecycleListener> findLifecycleListeners() {
        return null;
    }

    @Override
    public void removeLifecycleListener(LifecycleListener listener) {

    }
}
